package client

import (
	"encoding/json"
	"errors"
	"fmt"
	"go.uber.org/atomic"
	"net"
	"time"
	"gitee.com/viwii/vconf/module/broadcast"
)

type Config struct {
	IpStr string
}

type Client struct {
	stream broadcast.PacketStream
	id     int64
	waitId atomic.Int64
	waitFlag atomic.Bool
	monitorMap map[int64]func([]byte, error)
	waitChan chan *broadcast.ReplyMsg
}

func (c *Client) StartAccept()  {
	go func() {
		for c.waitFlag.Toggle() {
			pkg,err := c.stream.Read()

			if err!=nil{
				break
			}

			replyMsg := new(broadcast.ReplyMsg)
			err = json.Unmarshal(pkg.Data, replyMsg)

			if err != nil {
				fmt.Println("connect err:", err)
			}

			if replyMsg.Method == "monitor"{
				if cfunc,ok:=c.monitorMap[replyMsg.MsgID];ok{
					cfunc(replyMsg.Content,nil)
				}

				continue
			}

			if c.waitId.Load()!= -1 && c.waitId.Load() == replyMsg.MsgID{
				c.waitChan<- replyMsg
			}
		}

	}()
}

func (c *Client) New(cfg *Config) {
	c.monitorMap = make(map[int64]func([]byte, error))
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", cfg.IpStr)
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		fmt.Println("connect err:", err)
		return
	}

	c.stream = broadcast.NewPacketStream(conn, false)
	c.waitFlag.Store(true)
	c.StartAccept()
}

func (c *Client) DownLoadFile(appName string, clusterName string, namespaceName string) ([]byte, error) {
	msg := broadcast.RequestArg{
		Method:        "get",
		AppName:       appName,
		ClusterName:   clusterName,
		NamespaceName: namespaceName, //命名空间名
		MsgID:         c.id,
	}

	c.id++

	data, _ := json.Marshal(&msg)
	c.stream.Write(&broadcast.Packet{Data: data})

	c.waitId.Store(msg.MsgID)
	wt := time.NewTicker(3*time.Second)
	select {
		case pkg:=<-c.waitChan:
			{
				c.waitId.Store(-1)
				if pkg.Status == 0 {
					return pkg.Content, nil
				}
			}
			case <-wt.C:
				{
					c.waitId.Store(-1)
					return []byte{}, errors.New("超时3秒")
				}
	}

	return []byte{},nil
}

func (c *Client) DownLoadFileAndMonitor(appName string, clusterName string, namespaceName string, callBack func([]byte, error)) ([]byte, error) {
	dataOld, err := c.DownLoadFile(appName, clusterName, namespaceName)
	if err != nil {
		return dataOld, err
	}

	msg := broadcast.RequestArg{
		Method:        "sub",
		AppName:       appName,
		ClusterName:   clusterName,
		NamespaceName: namespaceName, //命名空间名
		MsgID:         c.id,
	}

	c.id++

	data, _ := json.Marshal(&msg)
	c.stream.Write(&broadcast.Packet{Data: data})

	pkg, err := c.stream.Read()
	if err != nil {
		fmt.Println("connect err:", err)
		return []byte{}, err
	}

	replyMsg := new(broadcast.ReplyMsg)
	err = json.Unmarshal(pkg.Data, replyMsg)

	if err != nil {
		fmt.Println("connect err:", err)
		return []byte{}, err
	}

	if replyMsg.MsgID != msg.MsgID || replyMsg.Status != 0 {
		return []byte{}, errors.New("check id err")
	}

	c.monitorMap[msg.MsgID]=callBack

	return dataOld,nil
}

func (c *Client)ShutDown(){
	c.waitFlag.Store(false)
}
